ApplicationContextAware

@VERO
Created Date · 2023년 09월 19일 02:09
Last Updated Date · 2023년 10월 02일 14:10

ApplicationContextAware 란?

정말 간단한 구조를 가지고 있는 인터페이스이다.

package org.springframework.context;  
  
import org.springframework.beans.BeansException;  
import org.springframework.beans.factory.Aware;  
  
public interface ApplicationContextAware extends Aware {  
  
    void setApplicationContext(ApplicationContext applicationContext) throws BeansException;  
  
}

Interface to be implemented by any object that wishes to be notified of the ApplicationContext that it runs in.

실행중인 ApplicationContext 에 대한 알림(?) 을 받고자 하는 객체가 구현할 인터페이스라고 한다.

번역이 이상하지만 아무튼 런타임에 ApplicationContext 가 필요한 객체가 구현하면 된다.
HandlerMapping, ViewResolver, DispatcherServlet 과 같이 런타임에 동적으로 빈들의 의존관계가 필요한 클래스가 구현한다.

Implementing this interface makes sense for example when an object requires access to a set of collaborating beans. Note that configuration via bean references is preferable to implementing this interface just for bean lookup purposes.

공식 docs 에서도 빈 조회 목적으로만 구현하는 것보다, 빈 참조를 통해 구성하기 위한 용도로 사용하는 것이 더 바람직하다고 적혀있다.

어떻게 주입되나?

예시로 AbstractHandlerMapping 이라는 클래스를 보자.
initApplicationContext 에서 initInterceptor 로 초기화를 하는 것을 확인할 수 있다.

여기서 obtainApplicationContext() 를 수행하게 되는데,

public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport  
       implements HandlerMapping, Ordered, BeanNameAware {  
  
    /** Dedicated "hidden" logger for request mappings. */  
    protected final Log mappingsLogger =  
          LogDelegateFactory.getHiddenLog(HandlerMapping.class.getName() + ".Mappings");  
  
  
    @Nullable  
    private Object defaultHandler;
    ...

	@Override  
protected void initApplicationContext() throws BeansException {  
    extendInterceptors(this.interceptors);  
    detectMappedInterceptors(this.adaptedInterceptors);  
    initInterceptors();  
}  
  
protected void extendInterceptors(List<Object> interceptors) {  
}  
  
protected void detectMappedInterceptors(List<HandlerInterceptor> mappedInterceptors) {  
    mappedInterceptors.addAll(BeanFactoryUtils.beansOfTypeIncludingAncestors(  
          obtainApplicationContext(), MappedInterceptor.class, true, false).values());  // ApplicationContext 를 받아온다.  
}  
   
protected void initInterceptors() {  
    if (!this.interceptors.isEmpty()) {  
       for (int i = 0; i < this.interceptors.size(); i++) {  
          Object interceptor = this.interceptors.get(i);  
          if (interceptor == null) {  
             throw new IllegalArgumentException("Entry number " + i + " in interceptors array is null");  
          }  
          this.adaptedInterceptors.add(adaptInterceptor(interceptor));  
       }  
    }}
}

WebApplicationObjectSupport 는 다시 ApplicationObjectSupport 를 상속한다.
위에서 언급되었던 obtainApplicationContext() 를 수행하는 것을 볼 수 있다.

public abstract class WebApplicationObjectSupport extends ApplicationObjectSupport implements ServletContextAware {  
  
    @Nullable  
    private ServletContext servletContext;  
  
  
    @Override  
    public final void setServletContext(ServletContext servletContext) {  
       if (servletContext != this.servletContext) {  
          this.servletContext = servletContext;  
          initServletContext(servletContext);  
       }  
    }

	...

	protected final ApplicationContext obtainApplicationContext() {  
	    ApplicationContext applicationContext = getApplicationContext();  
	    Assert.state(applicationContext != null, "No ApplicationContext");  
	    return applicationContext;  
	}
}

getApplicationContext() 메서드는 ApplicationObjectSupport 에 구체적으로 구현되어있다.

public abstract class ApplicationObjectSupport implements ApplicationContextAware {  
  
    /** Logger that is available to subclasses. */  
    protected final Log logger = LogFactory.getLog(getClass());  
  
    /** ApplicationContext this object runs in. */  
    @Nullable  
    private ApplicationContext applicationContext;  
  
    /** MessageSourceAccessor for easy message access. */  
    @Nullable  
    private MessageSourceAccessor messageSourceAccessor;

	...
    
	@Override  
	public final void setApplicationContext(@Nullable ApplicationContext context) throws BeansException {  
	    if (context == null && !isContextRequired()) {  
	       // Reset internal context state.  
	       this.applicationContext = null;  
	       this.messageSourceAccessor = null;  
	    }  
	    else if (this.applicationContext == null) {  
	       // Initialize with passed-in context.  
	       if (!requiredContextClass().isInstance(context)) {  
	          throw new ApplicationContextException(  
	                "Invalid application context: needs to be of type [" + requiredContextClass().getName() + "]");  
	       }  
	       this.applicationContext = context;  
	       this.messageSourceAccessor = new MessageSourceAccessor(context);  
	       initApplicationContext(context);  
	    }  
	    else {  
	       // Ignore reinitialization if same context passed in.  
	       if (this.applicationContext != context) {  
	          throw new ApplicationContextException(  
	                "Cannot reinitialize with different application context: current one is [" +  
	                this.applicationContext + "], passed-in one is [" + context + "]");  
	       }  
	    }}
}

BeanFactoryUtils 란?

위 코드를 자세하게 보면 이런 코드가 있다.

    mappedInterceptors.addAll(BeanFactoryUtils.beansOfTypeIncludingAncestors(  
          obtainApplicationContext(), MappedInterceptor.class, true, false).values());  

이러면 BeanFactoryUtils 가 빈을 가지고 있는 것 아닐까? 좀 더 알아보자.

BeanFactoryUtilsApplicationContext 를 파라미터로 받고 있다.
ApplicationContext 는 굉장히 여러 가지를 상속 받고 있는데, 그 중에 하나가 ListableBeanFactory 이다.

ListableBeanFactory 는 다음과 같은 클래스이다.
딱 봐도 복잡한 빈 관련 기능들을 수행하는 것을 볼 수 있다.

package org.springframework.beans.factory;  
  
import java.lang.annotation.Annotation;  
import java.util.Map;  
import java.util.Set;  
  
import org.springframework.beans.BeansException;  
import org.springframework.core.ResolvableType;  
import org.springframework.lang.Nullable;  
  
public interface ListableBeanFactory extends BeanFactory {  
  
    boolean containsBeanDefinition(String beanName);  
  
    int getBeanDefinitionCount();  
  
    String[] getBeanDefinitionNames();  
  
    <T> ObjectProvider<T> getBeanProvider(Class<T> requiredType, boolean allowEagerInit);  
  
    <T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType, boolean allowEagerInit);  
  
    String[] getBeanNamesForType(ResolvableType type);  
  
    String[] getBeanNamesForType(ResolvableType type, boolean includeNonSingletons, boolean allowEagerInit);  
  
    String[] getBeanNamesForType(@Nullable Class<?> type);  
  
    String[] getBeanNamesForType(@Nullable Class<?> type, boolean includeNonSingletons, boolean allowEagerInit);  
  
    <T> Map<String, T> getBeansOfType(@Nullable Class<T> type) throws BeansException;  
  
    <T> Map<String, T> getBeansOfType(@Nullable Class<T> type, boolean includeNonSingletons, boolean allowEagerInit)  
          throws BeansException;  
  
    String[] getBeanNamesForAnnotation(Class<? extends Annotation> annotationType);  
  
    Map<String, Object> getBeansWithAnnotation(Class<? extends Annotation> annotationType) throws BeansException;  
  
    <A extends Annotation> A findAnnotationOnBean(String beanName, Class<A> annotationType)  
          throws NoSuchBeanDefinitionException;  
  
    <A extends Annotation> A findAnnotationOnBean(  
          String beanName, Class<A> annotationType, boolean allowFactoryBeanInit)  
          throws NoSuchBeanDefinitionException;  
  
    <A extends Annotation> Set<A> findAllAnnotationsOnBean(  
          String beanName, Class<A> annotationType, boolean allowFactoryBeanInit)  
          throws NoSuchBeanDefinitionException;  
  
}

BeanFactoryUtils.beansOfTypeIncludingAncestors 를 좀 더 자세히 알아보자. 해당 메서드를 호출하면 다음과 같은 코드가 실행된다.

아래는 BeanFactoryUtils.beansOfTypeIncludingAncestors 코드이다.

public static <T> Map<String, T> beansOfTypeIncludingAncestors(  
       ListableBeanFactory lbf, Class<T> type, boolean includeNonSingletons, boolean allowEagerInit)  
       throws BeansException {  
  
    Assert.notNull(lbf, "ListableBeanFactory must not be null");  
    Map<String, T> result = new LinkedHashMap<>(4);  
    result.putAll(lbf.getBeansOfType(type, includeNonSingletons, allowEagerInit));  
    if (lbf instanceof HierarchicalBeanFactory hbf) {  
       if (hbf.getParentBeanFactory() instanceof ListableBeanFactory pbf) {  
          Map<String, T> parentResult = beansOfTypeIncludingAncestors(pbf, type, includeNonSingletons, allowEagerInit);  
          parentResult.forEach((beanName, beanInstance) -> {  
             if (!result.containsKey(beanName) && !hbf.containsLocalBean(beanName)) {  
                result.put(beanName, beanInstance);  
             }  
          });  
       }  
    }    return result;  
}

주어진 타입의 모든 빈 인스턴스를 ListableBeanFactory 와 해당 팩토리의 부모 계층에서 찾는 데 사용되는 메서드이다. 특히 계층적인 BeanFactory 에서 유용하다.

즉, ApplicationContextListableBeanFactory 를 상속하고, refresh 에서 빈을 초기화하는 과정이 일어난다. BeanFactoryUtils 는 이런 빈들을 찾아오는 과정들을 용이하게 해주는 클래스라고 생각하면 된다.

refresh 함수에 대한 자세한 부분은 AbstractApplicationContextrefresh 메서드를 참고하면 된다.
약간의 설명은 application-context 에 적어두었다.